home *** CD-ROM | disk | FTP | other *** search
/ Super Shareware Collection / Super Shareware Collection.iso / os_2 / pm22x333.zip / PMODE.DOC < prev    next >
Text File  |  1994-02-21  |  53KB  |  1,024 lines

  1. Warning:
  2.   This document is intended for the experienced programmer with knowledge of
  3. a good deal of assembly and the 386. If you are not one such person, the text
  4. that follows will be very confusing to you. Read on if you wish... You have
  5. been warned.
  6.  
  7. ------------------------------------------------------------------------------
  8. Some crap:
  9.  
  10.   This is a very messy doc, self-contradicting in many places because it went
  11. from describing 1.29a to 2.1232 to 2.2x333. And since I'm so lazy, I did not
  12. rewrite the whole thing for each version. Merely touched up the doc and
  13. appended new information. If two things I say in this doc conflict, trust the
  14. newer information. And above all, experiment for yourself. Almost the entire
  15. doc describes PMODE v1.29a. Information for 2.1232, and then 2.2x333 is down
  16. at the bottom.
  17.  
  18.   This code, as well as this doc (damn I hate writing dox), were written up
  19. by Tran (a.k.a. Thomas Pytel) formerly of Renaissance. It is intended for all
  20. those of you out there who would like to code in protected mode, but keep
  21. running up against obstacles. If you want to use this thing as it is though,
  22. you will have to code in 100% ASM. But hey, that's no problem. I do it all the
  23. time. I did write this thing for myself. So if you have a problem with it,
  24. tough. You have a few options. You can use it as it is, and code in all ASM
  25. (not a terribly difficult feat). You can examine the code, and use what you
  26. learn to code up your own protected mode system, one that you can maybe throw
  27. into a nice high level language maybe. Or you can just ignore this code and
  28. this doc.
  29.  
  30. ------------------------------------------------------------------------------
  31. Intro:
  32.  
  33.   Ah... many are the joys and woes of this chip we all know as the 386. The
  34. 486 - bah, just a 386 with a cache. The 586 - hahaha, two 486s with double the
  35. cache. But the wonderful 386 architecture. That finally gives us our needed
  36. flat memory. Not to mention the paging (not a personal favorite, but VERY
  37. useful). It's been around here for a while now, but it hasn't been used very
  38. much. I'm assuming you know all the problems associated with sharing the
  39. 386 protected mode amongst different programs. So I'll just go on to tell ya
  40. about two methods of doing this, VCPI and DPMI.
  41.  
  42.   In the beginning (or at least a long time ago). There was the EMS and the
  43. XMS. Two pathetic attempts by the hopelessly crippled (design-wise) 8086s and
  44. 286s. EMS and XMS were fine for accessing small chunks of a larger address
  45. space at a time. These methods were usually very slow. As for XMS, the 286
  46. actually had to copy memory every time it was requested. And hardware EMS
  47. resided on the slow system bus (well, not that much slower compared to the
  48. speed of those machines). With the introduction of the 386, and soon the
  49. memory managers, things changed. EMS and XMS could now be handled with the
  50. on-chip paging mechanism. Which was faster than the memory copy method, and
  51. even the hardware EMS. But for this task, the memory managers had to run the
  52. system in V86 mode. Forsaking all other programs which would want to take
  53. control of the 386 protected mode.
  54.  
  55.   Thus was devised VCPI. A method of allowing programs running in the V86
  56. system to switch to protected mode. After switching to protected mode, those
  57. programs would have total control of the system. Running at a privilege level
  58. of 0. They could communicate with the other protected mode program, the VCPI
  59. server, through a common memory address space starting with the first megabyte
  60. of memory. VCPI was a superset of the EMS standard, and was implemented in
  61. virtually all memory managers and 386 extenders. But VCPI was woefully
  62. inadequate for the multitaskers that were soon to follow.
  63.  
  64.   Then there came forth the DPMI standard. Born from the window, it is the
  65. epitome of total lameness. Many are the unknowing peons who run the window.
  66. And many are the lamer who do not know the purpose of our favorite key
  67. combination, the CTRL+ALT+DEL. A wondrous function of that eternal TSR called
  68. the BIOS. Many a fool activate it whilst on a local BBS, erroneously believing
  69. they will get free downloads. But I digress, we were all lame at one time or
  70. another. This standard of the DPMI was different from those of antiquity.
  71. This foul interface was designed with total control in mind. Total control
  72. by the evil host whose lameness was imposed upon all those running beneath it.
  73. At a lowly CPL3, this loathsome beast's clients are truly at a disadvantage.
  74. Their INs and OUTs under the thorough scrutiny of the master. Forced to
  75. grovel for precious memory. Not even their own instructions are sacred. For
  76. the fiend which is DPMI, patiently waits, and watches. Ready to tear the
  77. precious life from the pitiable client at the slightest sign of the forbidden
  78. CLI. Then the monster does its deeds, toggling an illusion of that which was
  79. once real. This virtual flag of interrupts ensures the depraved host total
  80. domination. So easy to hate it is this beast. But yet... In the name of
  81. multiple tasking, and system integrity. The DPMI continues to gain ground.
  82. (Well sure, if ya want those things. But where is your adventure... Your
  83. spirit... Have you totally forgotten what a good crash feels like???)
  84.  
  85.   These days, VCPI and DPMI permeate the world of 386 software. From memory
  86. managers, to Windows and OS/2. If something is protected, its using one of
  87. these. VCPI though, is on its way out. Being replaced by DPMI. And if you want
  88. to code protected mode... Unless you force upon the user a clean boot, you
  89. have to support one of these two standards.
  90.  
  91. ------------------------------------------------------------------------------
  92. So what you got here:
  93.  
  94.   Having coded raw flat protected mode, I am hooked. I shall not revert to
  95. real mode limitations. My previous protected mode system 'START32' was very
  96. intolerant of crap like VCPI and DPMI and EMS and XMS and everything else. It
  97. required a clean boot to function. But hey, for that I got pure wonderful
  98. flat protected mode. Unfortunately, the masses did not share my sentiment.
  99. And they whined about rebooting. Pitiful as they were, I have heard their
  100. cries. And I understand. The culmination is the code before you (or rather in
  101. the other file, PMODE.ASM). This is basically the same pure beautiful flat
  102. protected mode system as my old START32 system, except that it will run under
  103. VCPI and DPMI and XMS (albeit a little slower).
  104.  
  105. ------------------------------------------------------------------------------
  106. What it thinks it does:
  107.  
  108.   The point of this protected mode header thingy is to provide a simple, flat
  109. mode environment for easy assembly coding. PMODE will take care of detecting
  110. a 386, the type of system (raw,XMS,VCPI,or DPMI) and making sure there is
  111. enough memory, both low and high. The protected mode code runs in one big
  112. segment, the size of which is infinite (or 4GB, whichever is larger). You can
  113. call real mode interrupts of far routines from your pmode code. And those
  114. in turn can call back to protected mode. And on and on like this to the limits
  115. of the stack. Once in pmode, all normal IRQs are still active and are by
  116. default, redirected to their real mode handlers. You can intercept these
  117. IRQs for your own use, then pass them on to the real mode handlers or not.
  118.  
  119.   This system was coded with speed in mind. It is meant for stuff like games,
  120. demos (as in the ones with cool muzik and grafix), or stuff that needs a lot
  121. of memory. What you should not attempt from this system, is going TSR, or
  122. executing DOS programs. This system runs absolutely perfectly for what it was
  123. meant for in raw and XMS. There are some minor problems under VCPI and DPMI
  124. though, and i'll list them later. But right now...
  125.  
  126. ------------------------------------------------------------------------------
  127. First of all, the structure:
  128.  
  129.   In PMODE.ASM, there are three segments. They are as follows:
  130.  
  131. ) CODE16 - A 16bit segment that holds all the real mode and 16bit protected
  132.            mode init and exit code. I'm not sure (I've forgotten by now), but
  133.            you should probably leave it as the first segment of the EXE.
  134. ) CODE32 - The huge 32bit segment. You can throw in as much code as will fit
  135.            in low memory (no 64k fixup overflows). If you need more code
  136.            space, you're gonna have to load it into extended memory at runtime
  137.            and use it there. In pmode, addresses (code and data, they're the
  138.            same memory space) are offset from the beginning of this segment.
  139.            If you want to address something below the beginning of this seg,
  140.            you go about it in a different way. I'll explain later.
  141. ) CODEEND - This MUST be the last segment in the EXE. It is the base for the
  142.             stack and low memory allocation.
  143.  
  144.   The stack is an interesting animal (or was it a mineral¿). But anyway, you
  145. don't have to worry about the stack. The header will set it up to whatever
  146. you specify (up to a limit of 64k (there's a reason)). The same stack is
  147. shared by your pmode code and real mode calls (that's the reason). The stack
  148. always begins at CODEEND, and goes on for STAKLEN (a var declared at the top
  149. of PMODE.ASM) paragraphs. I will explain how its used by both pmode and real
  150. mode code later.
  151.  
  152.   There are two heaps you can allocate memory from at run time. Low memory,
  153. which is all conventional memory below A0000h. And extended memory, all the
  154. nice crap above 1M. There's a reason for keeping them separate (other than
  155. that big hole between A0000h and 100000h (which I did not want to fill in or
  156. rearrange with paging)). DOS can only see the low heap. So in calls where you
  157. have to pass buffer addresses, you must pass only buffers that you have in low
  158. memory. Low memory is the place where you would allocate any critical disk or
  159. DMA buffers (more on a few potential problems with DMA later).
  160.  
  161.   You call real mode interrupts and far routines with 'virtual registers'.
  162. Memory images of the registers as they will be set for the real mode int or
  163. proc. Code in real mode can call protected mode routines with those same
  164. 'virtual registers'.
  165.  
  166.   This thing has a few 'functions' for dealing with low and high memory and
  167. getting and setting IRQs. There are also some variables made available to ya
  168. which you'll find quite useful.
  169.  
  170. ------------------------------------------------------------------------------
  171. Details of runtime:
  172.  
  173.   I'm assuming you know all about selectors and descriptor tables and stuff
  174. like that. But going on... After it does all that it needs to, PMODE will
  175. jump to an external label in code32 called '_main'. This is where your 32bit
  176. code takes over. When you gain control:
  177.  
  178. ) The stack is all set up.
  179. ) The interrupts are disabled (and have been all the way from real mode) just
  180.   in case theres something you want to do first (like shut them all off at
  181.   21h and 0a1h, or replace all the vektorz (so what if I spell it vektorz)).
  182. ) CS points to the code segment you're running in (duh...).
  183. ) DS,ES,FS, and SS point to an alias of the code segment (same memory). (In
  184.   case you don't remember, you can NEVER write to a CS: override in protected
  185.   mode... Read, yes... But not write).
  186. ) GS is a segment that's just as big as the others (infinite, remember?...)
  187.   but starts at absolute 0. This is ofcourse useful for accessing the real
  188.   mode int vektor table, or the BIOS data area, or the PSP, etc...
  189.  
  190.  
  191. Selectors:
  192.  
  193.   There are three main selectors you have to know. '_selcode', '_seldata', and
  194. '_selzero' are 16bit word vars you can access to get the selector values for
  195. the code, data, and zero (GS) segments respectively. Their values are 8, 10h,
  196. and 18h under all protected systems except DPMI. As I said, on getting to
  197. _main, CS=_selcode, DS=ES=FS=SS=_seldata, and GS=_selzero. You can change the
  198. segregs if you wish (for example to do a REP MOVS in the zero seg). But the
  199. PMODE 'functions' and ints expect the segregs to be these values (except for
  200. the special case of SS, this will be explained later). And these must be the
  201. values when you jump to '_exit' to return to DOS. Another thing that is
  202. assumed by PMODE is DF=0 (direction flag is clear (like the CLD instruction)).
  203. This is because most string moves are forward. If you want to do a string move
  204. the other way, go ahead. Just do a CLD after.
  205.  
  206.  
  207. Linear addresses:
  208.  
  209.   As I said, in pmode, all addresses are relative to the beginning of the
  210. CODE32 segment (which could start anywhere in low memory). For this reason,
  211. you must adjust any physical memory pointers before you use them. That is,
  212. to access something at B8000h (B800:0000, if you haven't noticed, I'm using
  213. all true linear addresses in this doc, no need for the seg:off crap). Anyway,
  214. If you want to write to B8000h, it will not be at DS:B8000, but at
  215. DS:(B8000h-linear address of the beginning of CODE32). And this linear address
  216. is stored in a variable called '_code32a'. So if the segment CODE32 was 1F43,
  217. the linear address would be 1F430h (seg*10h remember¿). So to get a pointer
  218. to B8000h, you would do something like:
  219.  
  220.   mov eax,0b8000h
  221.   sub eax,_code32a
  222.  
  223.   And just this macro is provided in PMODE.INC, as well as a macro to go the
  224. other way, relative address to physical. Ofcourse, if you address something
  225. with GS (assuming GS is _selzero), you can use the actual unadjusted linear
  226. address. The absolute linear address for CODE16 is also provided in
  227. '_code16a'. As well as the absolute linear address of the PSP in '_pspa'. The
  228. linear addresses '_code16a' and '_pspa' will always be less than '_code32a'.
  229. To access them (memory pointed to by them, these vars are in CODE32), you will
  230. have to use one of two methods. One is easy enough, just use the GS segment.
  231. Or you could use negative indexes from the normal segment. This relies on the
  232. 4G wraparound (don't worry, the limits of all segments in the descriptor table
  233. are 4G). Strange things may happen if the 686 doesn't support the 4G wrap, but
  234. from what I understand, the 586 is still limited (limited??? damn it, even CDs
  235. don't reach that) to 4G segments.
  236.  
  237.  
  238. Memory (dis)organization:
  239.  
  240.   As for the memory. You have those two heaps, low and high (extended) memory.
  241. Each of which is guaranteed to be at least as much as you specified in LOWMIN
  242. and EXTMIN at the top of PMODE.ASM. PMODE will hog up all low memory (because
  243. its meant to run standalone), and it will attempt to grab all the high memory
  244. it can. Two dwords hold information about each memory area. '_lomembase' and
  245. '_lomemtop' specify the base and top of the low memory pool as relative
  246. addresses (ready to use, no adjustment needed). The total amount of low memory
  247. available in bytes is _lomemtop-_lomembase (notice _lomemtop points to one
  248. byte beyond the last available byte). The _getlomem 'function' is a very
  249. simple routine that takes a length in EAX, and checks to see if there is
  250. enough low mem. If there is enough, it adds the length to _lomembase and
  251. returns a pointer (ready to use) in EAX to the low memory block along with the
  252. carry flag clear. If it finds that _lomemtop-_lomembase > length (not enough
  253. memory), it returns with the carry flag set and EAX undefined. '_himembase',
  254. '_himemtop', and '_gethimem' are the same thing for high mem as those other
  255. things are for low mem. Just to make sure you understand this, here is some
  256. code, cuz as we all know, code speaks louder than words (actually, silence
  257. speaks louder than code, but what the hell).
  258.  
  259.   xor al,al
  260.   mov edi,_lomembase            ; fill all available low memory with 0
  261.   mov ecx,_lomemtop
  262.   sub ecx,edi
  263.   rep stosb
  264.   mov edi,_himembase            ; ditto for high memory
  265.   mov ecx,_himemtop
  266.   sub ecx,edi
  267.   rep stosb
  268.  
  269.   There is one other curiosity provided. '_getmem' will get a any block of
  270. memory. It will first check low memory, and if there is not enough, it will
  271. check high memory. If you wish, you can code yourself a little 'malloc'
  272. library that will deal with blocks, and provide you with all the joys of
  273. fragmentation.
  274.  
  275.  
  276. Calling icky real mode:
  277.  
  278.   I did say you can call real mode, and back. Let me first say that this is
  279. only provided so that you can call real mode interrupts, and routines that
  280. you don't want to recode in protected mode. I would not suggest making it a
  281. habit of coding across modes. Except maybe if you do a driver that you also
  282. want to work from real mode. But anyway... You can call real mode interrupts
  283. or procedures from protected mode through INT 32h (call real mode far proc)
  284. and INT 33h (call real mode int). These interrupts are only available to the
  285. protected mode part of your program. In real mode, there is a separate INT 32h
  286. that calls a protected mode routine. Don't confuse the two INT32s with each
  287. other, though they do basically the same thing. To pass register values across
  288. modes, you use 'virtual registers' (I just love that word... virtual... haha).
  289. These 'virtual registers' are merely memory images of EAX,EBX,ECX,EDX,ESI,EDI,
  290. EBP,DS,ES,FS,and GS. AL and AH and AX and BL ... etc ... are there too, and
  291. they share the appropriate memory space with each other so if you change the
  292. 'virtual' AH register, the 'virtual' AX and EAX registers will be changed
  293. accordingly. You'll notice there are no SS,ESP,CS,EIP registers. CS:EIP is
  294. taken from the real mode interrupt vektor table for int calls, and passed in
  295. the real CX:DX registers for a procedure call. SS:ESP is set up in the master
  296. stack used by PMODE. Which is the stack your program runs on. I'll explain the
  297. stack handling in detail l8r. Heres a breakdown of the ints:
  298.  
  299. ) INT 33h from pmode: Do a real mode interrupt.
  300.   AL=interrupt you want to do. All V86R_??? general and segment registers will
  301.   be passed to the real mode handler. They will also be passed back as the
  302.   return values. The carry, zero, aux, parity, sign, and overflow flags will
  303.   be passed back as the actual CPU flags. The real mode interrupt will be
  304.   called with interrupts disabled (as it is usually). Keep in mind, no CPU
  305.   registers will be modified (except the flags mentioned). Only their V86R_???
  306.   images will be changed by the real mode int handler.
  307.  
  308. ) INT 32h from pmode: Call a real mode far procedure.
  309.   CX:DX=seg:off you want to call. The register passing works just like INT33.
  310.   Except that the interrupt flag will be preserved across the call to real
  311.   mode (but not back, the IF flag will be in the same state as it was before
  312.   the int).
  313.  
  314. ) INT 32h from real mode: Call a pmode procedure.
  315.   EDX=off. A 32bit offset in the CODE32 segment. The register passing works
  316.   just like for the other INT32 in pmode, except that segregs are not passed
  317.   to or from the pmode routine. Upon entry to the routine, the system standard
  318.   thingys are set. That is:
  319.     CS=_selcode, DS=ES=FS=SS=_seldata, GS=_selzero, DF=0 (CLD).
  320.   And they must be that when the thing executes its RET (not RETF).
  321.  
  322.  
  323. DPMI takes its toll and IRQs:
  324.  
  325.   Upon startup, all the interrupt vektors for IRQs point to routines that
  326. redirect the IRQs to their default real mode handlers. You can hook into any
  327. IRQ you want. There are two dword pointers that allow you to get and set IRQ
  328. vektorz. '_getirqvect' and '_setirqvect' point to routines to get and set the
  329. linear address of the handler for specific IRQs within the CODE32 segment. To
  330. get the address of a handler, just do a 'call _getirqvect' with BL set to the
  331. IRQ num you want (0-15). EDX will be returned pointing to its current handler.
  332. To set an IRQ, pass BL again as the IRQ number, and EDX as the offset of the
  333. new handler. You can chain to the old handler if you want just by jumping to
  334. the old address when your handler is done processing.
  335.  
  336.   When your IRQ handler is called, you can be sure of only one thing. The IF
  337. flag is clear. All the general regs and segregs should be treated as
  338. undefined. It would seem logical that if SS=_seldata throughout the entire
  339. code, it will be that in the IRQ. And this is true under all systems except
  340. DPMI. DPMI deems it fit to switch onto another stack. And that's the stack
  341. your handler must be on when it does its IRETD. You can switch stacks during
  342. processing if you want, but I really don't suggest doing that. Also, if you
  343. intend to call INT32/33 from your handler (you can, they are all totally
  344. reenterant), you must be on that stack (DPMI requirement of a mode switch it
  345. seems. I've tried to switch onto another stack (yes locked) and switch to
  346. real mode (using DPMI raw mode switching and state saving) but DPMI dies if
  347. another IRQ goes off in real mode).
  348.  
  349.   Another consideration for DPMI is the IF flag. According to DPMI specs, only
  350. CLI, STI, and INT 31h functions AX=900h and AX=901h should be counted on to
  351. modify the interrupt flag (POPF(D) and IRET(D) should not). This is because
  352. certain DPMI systems might have to virtualize the interrupt flag, and keep the
  353. real flag enabled at all times (but don't worry, if the 'virtual' flag is
  354. clear, your program will not get any IRQs). In practice, certain DPMIs do
  355. allow IRET(D)s and POPF(D)s to modify the virtual interrupt flag. But this is
  356. inconsistent across them. So if you want DPMI compatibility (you probably do,
  357. or you would not be using this code), you should follow these rules:
  358.  
  359. ) CLI and STI are allowed, and do their functions.
  360. ) Don't assume anything about POPF(D) and IRET(D) and the interrupt flag.
  361. ) Don't assume the interrupt flag PUSHF(D) stores on the stack is correct,
  362.   it might be the real flag or the 'virtual' flag.
  363. ) These DPMI INT 31h functions are supported under all systems.
  364.   ) AX=900h: Get state of IF and disable it. Returns AL set to the IF flag.
  365.   ) AX=901h: Get state of IF and enable it. Returns AL set to the IF flag.
  366.   ) AX=902h: Only returns AL set to the IF flag (0=disabled, 1=enabled).
  367. ) At the end of an IRQ handler, put a STI. When the handler is called, flags
  368.   are automatically disabled. And if you do not reenable them, and neither
  369.   does the IRETD... Well... you get the point.
  370.  
  371.  
  372. The stack entity:
  373.  
  374.   PMODE uses one major stack for both pmode and real mode. This stack is
  375. always located in low memory (always locked under DPMI). The size of the stack
  376. is set at the top of PMODE.ASM in the var 'STAKLEN'. There is another var
  377. there called 'STAKSAFE', and this is what must be explained.
  378.  
  379.   When a mode switch occurs, the new stack is the old stack base minus
  380. STAKSAFE paragraphs. The stack base is the stack location when your program
  381. starts. And it is only modified by mode switches. An example is in order here.
  382.  
  383.   Say your program starts, and you have a STAKLEN of 100h (1000h bytes) and
  384. a STAKSAFE of 20h (200h bytes). After you have pushed a few values, and are
  385. about to call real mode, the stack has gone down to 0F30h. After the mode
  386. switch, the location in the stack will be 0E00h (1000h-200h). Now in real mode
  387. you push some values. And the stack goes down to 0DE0h. Then you make a call
  388. to protected mode. The protected mode stack will start at 0C00h (0E00h-200h).
  389. After the return to real mode, the stack will be back to 0DE0h. You pop your
  390. values and return again. Back in protected mode, the stack will be where it
  391. was before the initial call to real mode, at 0F30h. So STAKSAFE is the maximum
  392. stack size that is safe from modification if a mode switch occurs. But beware,
  393. an IRQ that goes off that is redirected to the other mode is a mode switch.
  394. That is, an IRQ in protected mode that is redirected to real mode will cause
  395. the stack change. As will ANY IRQ in real mode (since it temporarily goes to
  396. pmode, and is redirected back to real mode). An IRQ that occurs in pmode, and
  397. is NOT sent to real mode is NOT a mode switch.
  398.  
  399.   Under DPMI things are slightly different. DPMI handles its own stack
  400. switching. Any IRQ causes a switch to a totally different stack. It gets a
  401. little complicated as DPMI does its switching among 4 different stacks. I
  402. was not able to put in my own IRQ redirectors to real mode, so you have to
  403. rely on DPMIs redirectors (which in some cases don't redirect the IRQs as they
  404. should). It seems even DPMI has problems managing its own stacks. Perhaps I
  405. missed some critical little point. But I don't think so... Even with state
  406. saving and the raw mode switching, if I switched off the stack the DPMI host
  407. provided for an IRQ, then jumped to real mode, and another IRQ occurred...
  408. Well... Let's just say that was the end of that. This may be a little
  409. confusing, so let me summarize what will keep you safe:
  410.  
  411. ) In an IRQ handler, DON'T switch off the stack it is entered with. Which is
  412. not guaranteed that SS=_seldata.
  413. ) Don't do more nested calls across modes than (STAKLEN/STAKSAFE)-1. (-2 if
  414. you just want to be totally safe).
  415. ) You CAN safely assume SS=_seldata in protected mode only in your main stream
  416. of execution, and in routines that are called with INT32 from real mode.
  417. ) Consider your maximum effective stack size to be STAKSAFE, not STAKLEN.
  418. ) You CAN call across modes using INT32/33 from an IRQ handler in both real
  419. and protected mode (useful for that pesky mouse callback thingy).
  420.  
  421.  
  422. Exceptions:
  423.  
  424.   Are handled entirely by the DPMI host in those systems. In raw, XMS, and
  425. VCPI, exceptions 0, 1, 2, 5, and 7 are reflected to real mode just like IRQs
  426. would be (they are sent safely (actual real mode)). Exceptions 3, 4, 8 and
  427. 9-1fh cause immediate termination. There is no debug dump done. If you want
  428. though, you can put in your own. The main exception handler is 'cp_exc'
  429. somewhere around line 1828 in PMODE.ASM. As you can see from the little entry
  430. code above it, it is entered directly from an exception with all registers
  431. pushed and AL=exception number. And as you can see all it does is load up the
  432. system default thingies and jumps to _exit. Just to clarify, it loads up DS,
  433. ES, and FS with 10h and not _seldata simply because this is an exception
  434. handler only for raw, XMS, and VCPI. Under which _seldata always equals 10h.
  435.  
  436. ------------------------------------------------------------------------------
  437. The 'virtual' registers might be a little confusing:
  438.  
  439.   So here's some semi-sorta-pseudo-code.
  440.  
  441. all_genregs     = [EAX, EBX, ECX, EDX, ESI, EDI, EBP]
  442. all_segregs     = [DS, ES, FS, GS]
  443. all_regs        = [all_genregs, all_segregs]
  444. all_v_genregs   = [V86R_EAX, V86R_EBX, V86R_ECX, V86R_EDX,
  445.                    V86R_ESI, V86R_EDI, V86R_EBP]
  446. all_v_segregs   = [V86R_DS, V86R_ES, V86R_FS, V86R_GS]
  447. all_v_regs      = [all_v_genregs, all_v_segregs]
  448. IF_stat         = current interrupt flag status
  449. pre-int_IF_stat = interrupt flag status before the software INT 32/33
  450. other_IF_stat   = interrupt status to set for the procedure/int called
  451.  
  452. INT 32h from protected mode:
  453.   PUSH  all_genregs
  454.   PUSHF                         ; just for show, they are assumed to affect
  455.   CLI                           ; the interrupt flag.
  456.   MOV   other_IF_stat,pre-int_IF_stat
  457.   JMP   realmode
  458. backtopmode:
  459.   MOV   DS,_seldata
  460.   MOV   ES,_seldata
  461.   MOV   FS,_seldata
  462.   MOV   GS,_selzero
  463.   POPF
  464.   POP   all_genregs
  465.   IRETD
  466. realmode:
  467.   MOV   all_regs,all_v_regs
  468.   MOV   IF_stat,other_IF_stat
  469.   CALL  procedure
  470.   CLI
  471.   MOV   all_v_regs,all_regs
  472.   JMP   backtopmode
  473.  
  474. INT 33h from protected mode:
  475.   PUSH  all_genregs
  476.   PUSHF
  477.   CLI
  478.   JMP   realmode
  479. backtopmode:
  480.   MOV   DS,_seldata
  481.   MOV   ES,_seldata
  482.   MOV   FS,_seldata
  483.   MOV   GS,_selzero
  484.   POPF
  485.   POP   all_genregs
  486.   IRETD
  487. realmode:
  488.   MOV   all_regs,all_v_regs
  489.   CALL  interrupt
  490.   CLI
  491.   MOV   all_v_regs,all_regs
  492.   JMP   backtopmode
  493.  
  494. INT 32h from real mode:
  495.   PUSH  all_regs
  496.   PUSHF
  497.   CLI
  498.   MOV   other_IF_stat,pre-int_IF_stat
  499.   JMP   pmode
  500. backtorealmode:
  501.   POPF
  502.   POP   all_regs
  503.   IRET
  504. pmode:
  505.   CLD
  506.   MOV   DS,_seldata
  507.   MOV   ES,_seldata
  508.   MOV   FS,_seldata
  509.   MOV   GS,_selzero
  510.   MOV   all_genregs,all_v_genregs
  511.   MOV   IF_stat,other_IF_stat
  512.   CALL  procedure
  513.   CLI
  514.   MOV   all_v_genregs,all_genregs
  515.   JMP   backtorealmode
  516.  
  517. ------------------------------------------------------------------------------
  518. Potential DMA problems:
  519.  
  520.   As you know, the DMA controllers in the PC use all physical addresses.
  521. Nothing but the processor itself knows how linear memory is arranged in the
  522. physical memory banks. When paging is disabled, the relationship is very
  523. simple. The linear address is always the same as the physical address. But
  524. when you enable paging, that could get all screwed up. In raw mode and XMS,
  525. you don't have to worry about this since paging is disabled. But under VCPI
  526. and DPMI things are different. You can almost definately count on extended
  527. memory addresses not being consistent with their physical addresses. Low
  528. memory however, will usually map perfectly to its physical addresses. Unless
  529. the program is running in some sort of multitasking system. Then the chances
  530. are slim. The point is that you can't trust DMA much under VCPI and DPMI.
  531.  
  532.   There is something called VDS (Virtual DMA Specification). This is the
  533. recommended way of handling DMA under VCPI and DPMI. I don't know too much
  534. about it now though. Maybe in the future I'll put something in based on that
  535. for DMA stuff. But for now your options are:
  536.  
  537. ) Don't use DMA (Not too hard, except if you wanna do SB output).
  538. ) Try to use DMA normally. It will work in raw and XMS, and most probably
  539. under VCPI and DPMI if they are not multitasking your program.
  540. ) If you know how to use VDS, feel free... (real mode int calls, remember¿)
  541.  
  542. ------------------------------------------------------------------------------
  543. And now to discuss some of the finer points of raw mode:
  544.  
  545.   Which is when this thing does not find anything that is running the system
  546. in V86 mode, and it can do all of its own protection control. This is the best
  547. possible way this thing can be run. The protected mode system runs at a CPL0.
  548. All IRQs and ints are handled through interrupt gates rather than task gates
  549. for speed. There is no task switching done at all, even to go to V86 mode.
  550. Paging is disabled to avoid that extra little bit of overhead. All IRQs are
  551. by default redirected to real mode (not true under some DPMIs). Actually real
  552. mode is V86 mode, still under the control of PMODE. So any IRQs that happen
  553. while a real mode thingy is being processed are taken immediately to protected
  554. mode. Where if you have a handler, it gets control right away. There were some
  555. problems with my old 'START32' code and the A20. I was not waiting for it to
  556. go stable, and was not testing to make sure. This has been fixed. If the A20
  557. fails to enable through the standard AT method or the PS2 method, PMODE will
  558. quit with an error message. All DMA in raw mode is real, since ALL linear
  559. addresses are the actual physical addresses because paging is disabled. The
  560. interrupt flag is real. When you disable interrupts, they are disabled, and
  561. will not screw up anything that might be timing sensitive as well as interrupt
  562. sensitive.
  563.  
  564.   So if you need to do something like time the vertical or horizontal retrace
  565. on your monitor, this is the mode you (or whoever) should be running in.
  566.  
  567. ------------------------------------------------------------------------------
  568. XMS:
  569.  
  570.   Is basically raw mode. Except that instead of INT 15h AH=88h, the XMS driver
  571. is used to allocate and lock extended memory. There is only one potential
  572. problem (and this goes for raw mode as well). If something tries to go to
  573. protected mode while in a real mode interrupt call, it will screw up.
  574. Obviously because the system is already in protected mode under the control of
  575. PMODE. This mode switch attempt would usually be the result of the XMS driver
  576. trying to copy memory for something. Or a disk cache that uses extended mem.
  577.  
  578. ------------------------------------------------------------------------------
  579. VCPI:
  580.  
  581.   This is actually a slightly worse mode to run in than DPMI. True that VCPI
  582. is also basically raw mode. The CPL is 0, and there is nothing scrutinizing
  583. the execution of your code. Paging is enabled, but that is a minor detail.
  584. The problem comes with the way VCPI compatibility works. To call a real mode
  585. interrupt or procedure, PMODE has to pass protected mode control back to the
  586. VCPI server. This comes out to one thing. IRQs that occur in a real mode call
  587. will NOT make it to your protected mode handler. I'm sorry, there is nothing
  588. I can do about this, it's just the way VCPI works. Yes it is possible to go
  589. V86 yourself to service the real mode call. Believe me, I've tried it. But the
  590. problem is that under VCPI systems, most of the real mode stuff (including
  591. DOS) are very dependant on EMS. And if control is not passed back to the VCPI
  592. server, EMS will not function. In fact, most memory managers require that the
  593. server watch for the execution of a certain interrupt from real mode, and
  594. intercept it. The actual interrupt vector in the real mode table might point
  595. into limbo. But as long as the VCPI server is in control, it will be handled
  596. properly. There are a few ways you could work around this:
  597.  
  598. ) Do the IRQ handler in real mode. That way, it will always be called no
  599. matter what is in control. But this seems to defeat the purpose of protected
  600. mode. And if this is a timing critical IRQ, you have a problem because passing
  601. control from a program (PMODE) to the VCPI server to execute the real mode
  602. IRQ callback takes a bit of time. Not a terrible amount, but it is a delay.
  603.  
  604. ) Do the IRQ handler in protected mode, and keep real mode calls to a minimum.
  605. For example, disable all but the critical IRQs to your program. And try to
  606. handle as many as you can in pmode. (You can read the keyboard direct from the
  607. hardware can't you. And you do know how to output FFh to A1h and FDh to 21h).
  608. But remember one thing... When you go to do a real mode call (DOS file call or
  609. something else you can't do yourself). Whatever the hardware cause of your IRQ
  610. will still be active. And if an IRQ occurs in real mode, and there is no real
  611. mode handler for it. Well, you know... So you either put in some valid real
  612. mode handler that may merely set a flag that you have an IRQ to service. Or
  613. disable the source of the IRQ (mask it off at 21h or A1h).
  614.  
  615.   VCPI also has the little problem of the possibility of inconsistent linear
  616. with physical addresses. Which means DMA is screwed. Generally speaking,
  617. unless the VCPI is coming from a multitasking thingy like DesqView, the low
  618. memory addresses will be accurate. As I said, I'm thinking of ways to solve
  619. this little problem. But for now, if you want to do something that requires
  620. DMA. If you know how to work VDS, you can try it with the real mode int calls.
  621. Or you can tell whoever is running your program, that if it doesn't work, to
  622. do a clean boot.
  623.  
  624. ------------------------------------------------------------------------------
  625. DPMI:
  626.  
  627.   Actually, DPMI is not that bad for what it was designed to be. It could be
  628. a little more consistent across its implementations, but oh well. I am a
  629. game/demo/speed freak however. I don't like the overhead imposed by the paging
  630. and CPL3 (in CPL3, certain instructions have to be emulated by software...
  631. Luckily they are not very common instructions). And multitasking in general
  632. is not that hot when you're trying to do a timing critical action game. But
  633. we're stuck with what we're stuck with. I figure running in real mode under
  634. DPMI is even slower than protected mode.
  635.  
  636.   One really annoying problem with DPMI is that current implementations are
  637. far from perfect. QDPMI 1.01 for example, dies when an IRQ occurs in a pmode
  638. call from a real mode IRQ. DPMI dox say this shouldn't happen, and it doesn't
  639. under Windows 3.1 DPMI implementation. Thus, QDPMI 1.01 is buggy. And who
  640. knows how many other DPMIs out there.
  641.  
  642.   For those of you who know DPMI. I am using the raw mode switch routine to
  643. do cross mode calls with INT32/33. I am ofcourse also saving the state on the
  644. stack. So it is perfectly reenterant. Just as a minor note, DPMI converts the
  645. environment segment in the PSP to a protected mode selector. I convert it back
  646. to the segment after going pmode with DPMI. But since DPMI needs the selector
  647. for the final return to real mode, I put it back then. But you can count on it
  648. being a real mode type segment.
  649.  
  650.   Hmm, another little problem is that I'm not sure how many DPMIs out there
  651. actually reflect IRQs to real mode if they occur in protected mode. Windows
  652. 3.1 seems to send them all over as it should. QDPMI however sends IRQ1, but
  653. not IRQ0. And it also doesn't seem to pass IRQs that occur in real mode
  654. through their protected mode handlers. Again, Windows 3.1 does. And from what
  655. I read in the dox, the Windows way is the way DPMI is supposed to be done.
  656.  
  657. ------------------------------------------------------------------------------
  658. Some misc notes:
  659.  
  660. ) Under VCPI, this thing will map as much extended memory as it can, up to
  661. 60M, without allowing the page tables to use up more memory than would leave
  662. LOWMIN. Allocating up to 60M means there is an absolute highest amount of
  663. extended memory under VCPI of 59M (even if there is more available).
  664.  
  665. ) Yes, this thing modifies its own code.
  666.  
  667. ) Before exiting your program, you do NOT need to restore any IRQ vektorz
  668. (pmode that is, if you modified the real mode vektor table, you gotta restore
  669. it). And you do not have to restore the IRQ masks at 21h and A1h (PMODE stores
  670. them before jumping to _main, and restores them before exiting).
  671.  
  672. ) If youre gonna add other segments (16bit right¿), put them in between CODE16
  673. and CODE32 only if they're small enough to still allow access to CODE32 data
  674. from CODE16. Otherwise put them between CODE32 and CODEEND. You can also just
  675. stick your 16bit code in a CODE16 segment.
  676.  
  677. ) The '_ret' label is provided simply because there are usually a lot of jumps
  678. that go to a RET. Just a minor convenience for myself.
  679.  
  680. ) Yeah the code is a total mess. I did not know many of the workings of VCPI
  681. and DPMI when I started. But hey, at least its functional.
  682.  
  683. ) Remember that upon reaching '_main', interrupts are still disabled. Don't
  684. forget to do the STI.
  685.  
  686. ) I hope you realize with pmode IRQ handlers, you don't have the BIOS to
  687. redirect IRQ9 to IRQ2. So any device that uses IRQ2 will actually be using 9.
  688.  
  689. ) Remember that the INT31 AX=9?? flag functions are only available in pmode.
  690. Go ahead, use the PUSHFs and POPFs in real mode to alter the IF flag... And
  691. any DPMI host that can't handle that properly deserves to crash.
  692.  
  693. ) The one byte INT3 instruction and INTO are treated as exceptions in pmode,
  694. and cause immediate termination (unless you change that in PMODE.ASM). In real
  695. mode they are sent to their real mode handlers.
  696.  
  697. ) I would REALLY suggest not ever switching your stack in protected mode
  698. yourself.
  699.  
  700. ) This thing was coded under TASM 3.0. So if you have something different,
  701. don't blame me if it doesn't compile.
  702.  
  703. ) For those of you who didn't realize it. There are no memory free functions
  704. for low and high mem because all you have to do is subtract the amount you
  705. want to free from '_lomembase' or '_himembase'.
  706.  
  707. ------------------------------------------------------------------------------
  708. Heres a list of the vars provided by PMODE to your program:
  709.  
  710. _lomembase:dword
  711.   Low mem base for allocation (first free byte).
  712.  
  713. _lomemtop:dword
  714.   Top of low mem (last free byte +1).
  715.  
  716. _himembase:dword
  717.   High mem base for allocation (first free byte).
  718.  
  719. _himemtop:dword
  720.   Top of high mem (last free byte +1).
  721.  
  722. _pspa:dword
  723.   Absolute linear address of start of PSP.
  724.  
  725. _code16a:dword
  726.   Absolute linear address of start of CODE16.
  727.  
  728. _code32a:dword
  729.   Absolute linear address of start of CODE32 (32bit code offset from this).
  730.  
  731. _selcode:word
  732.   Code segment selector.
  733.  
  734. _seldata:word
  735.   Data segment alias selector for code.
  736.  
  737. _selzero:word
  738.   Data segment starting at absolute 0.
  739.  
  740. _irqmode:word
  741.   Bitmap for all 16 IRQs (actually 15, but were ignoring 2) of how they should
  742.   be redirected to their real mode handlers (this is new to PMODE 2.1232).
  743.  
  744. _sysbyte0:byte
  745.   System bits, they have the following meanings (if I work on this system,
  746.   in the future they may also contain information on DMA maybe).
  747.   bits:
  748.     0-1: 00=raw, 01=XMS, 10=VCPI, 11=DPMI
  749.     2-7: undefined
  750.  
  751. _getirqvect:dword
  752.   A pointer to the get IRQ function appropriate for the mode.
  753.   The function takes arguments as follows:
  754.     In:
  755.       BL - IRQ num (0-0fh)
  756.     Out:
  757.       EDX - offset of current IRQ handler
  758.  
  759. _setirqvect:dword
  760.   A pointer to the set IRQ function.
  761.     In:
  762.       BL - IRQ num (0-0fh)
  763.       EDX - offset of new IRQ handler to set
  764.  
  765. ------------------------------------------------------------------------------
  766. And now some 'functions'. Remember, when calling any of them, you should have:
  767.   CS=_selcode, DS=ES=FS=_seldata, GS=_selzero, DF=0 (CLD).
  768.  
  769. _getmem:near
  770.   Allocate any mem, (first cheks low, then high)
  771.   In:
  772.     EAX - size requested
  773.   Out:
  774.     CF=0 - memory allocated
  775.     CF=1 - not enough mem
  776.     EAX - linear pointer to mem or ? if not enough
  777.  
  778. _getlomem:near
  779.   Allocate some low mem
  780.   In:
  781.     EAX - size requested
  782.   Out:
  783.     CF=0 - memory allocated
  784.     CF=1 - not enough mem
  785.     EAX - linear pointer to mem or ? if not enough
  786.  
  787. _gethimem:near
  788.   Allocate some high mem
  789.   In:
  790.     EAX - size requested
  791.   Out:
  792.     CF=0 - memory allocated
  793.     CF=1 - not enough mem
  794.     EAX - linear pointer to mem or ? if not enough
  795.  
  796. _lomemsize:near
  797.   Get amount of free low mem
  798.   Out:
  799.     EAX - number of bytes free
  800.  
  801. _himemsize:near
  802.   Get amount of free high mem
  803.   Out:
  804.     EAX - number of bytes free
  805.  
  806. _getirqmask:dword
  807.   Get status of IRQ mask bit (at port 21h or A1h)
  808.   In:
  809.     BL - IRQ num (0-15)
  810.   Out:
  811.     AL - status: 0=enabled, 1=disabled
  812.  
  813. _setirqmask:dword
  814.   Set status of IRQ mask bit
  815.   In:
  816.     BL - IRQ num (0-15)
  817.     AL - status: 0=enabled, 1=disabled
  818.  
  819. _exit:near
  820.   Exit to real mode
  821.  
  822. ------------------------------------------------------------------------------
  823. And now, 2.1232:
  824.  
  825.   Aside from a few typos in the doc, PMODE v1.29a was imperfect in another
  826. way. In raw/XMS mode, it executed real mode calls in V86 mode. Locking out
  827. anything in the real mode system that needed to switch to protected mode
  828. temporarily. This could be the XMS manager or a disk cache usually. And under
  829. VCPI it executed all real mode calls by giving control back to the VCPI server
  830. under all conditions. Sometimes this might not be necessary, just to execute
  831. a minor real mode routine maybe. PMODE v2.1232 allows you to control what type
  832. of real mode call is done. Whether the call is executed in V86 mode under the
  833. control of PMODE, or if the system is switched back to actual real mode (or
  834. control passed back to the server in the case of VCPI).
  835.  
  836.   I've added two new interrupts in pmode, INT 34h and 35h. Which have the same
  837. functions as INTs 32h and 33h respectively. The difference is that INT 32h and
  838. 33h are the safe way to do the real mode calls. That is they actually return
  839. to real mode (pass control back to the server under VCPI) to handle their
  840. function. INTs 34h and 35h execute the real mode calls by switching the system
  841. to V86 mode and keeping PMODE in control. This has the advantage of keeping in
  842. place all your protected mode IRQ handlers. If you do one of the safe calls,
  843. the entire protected mode system is put on hold while the call executes. INT
  844. 32h from real mode still does what it is supposed to. No matter if it executed
  845. from a safe or a V86 real mode call.
  846.  
  847.   The way IRQs are redirected to their real mode handlers can also be
  848. controlled by you. The '_irqmode' word defines how each of the 16 IRQs are
  849. redirected to real mode. Bits 0-15 stand directly for each of the 16 IRQs. A 1
  850. in an IRQ bit means that the IRQ will be redirected safely (switch to actual
  851. real mode). A 0 means the system will switch to V86 mode to do the IRQ handler
  852. under the control of PMODE. '_irqmode' starts out with all bits set (all IRQs
  853. are redirected safely).
  854.  
  855.   PMODE 2.1232 no longer reprograms the interrupt controllers for different
  856. base vectors for the hardware IRQs. PMODE 1.29a did that to relocate the low
  857. 8 IRQs from the 386 exception vectors. Since it always did real mode calls in
  858. V86 mode, it could always redirect the new vectors back to their default real
  859. mode values. But 2.1232 can actually return to real mode where the IRQs cannot
  860. be redirected through the pmode IDT. And doing 16 real mode IRQ redirectors
  861. might not be possible if VCPI remaps the vectors itself. And I don't like the
  862. idea of reprogramming the interrupt controllers every time an actual mode
  863. switch occurs (pmode/real, not pmode/V86). The way I did it now came out to be
  864. the best option in my mind. Any exceptions that are overridden by IRQs are
  865. lost. That is, any exception that has been overridden by an IRQ will be sent
  866. to that IRQ erroneously. The usual exceptions that are overridden are 8-0fh.
  867. All of which are terminal faults which you should not get in the first place.
  868. There is one exception that is treated in a special way. Exception 13 is
  869. needed for V86 real mode calls. As a result, it will always be on interrupt
  870. vector 13. Even if an IRQ overrides it (usually 5). But the IRQ will not be
  871. lost. When the handler for exception 13 gets control, it checks whether the
  872. source of an int is an IRQ or an actual exception. For you speed freaks out
  873. there, the check and redirection to IRQ handler in case of an IRQ takes 3
  874. instructions. Too bad, normally all pmode IRQ handlers get control as soon as
  875. physically possible (and appropriate to the system type (DPMI or not)).
  876.  
  877.   If you didn't entirely understand that last paragraph (and I don't blame
  878. you). Let me sum it up. You can get and set and enable and disable all 16 IRQs
  879. as usual. Except that there will usually be a 3 instruction delay on IRQ5 in
  880. getting to its handler. Exceptions 8, 9, 10, 11, 12, 14, and 15 will not make
  881. it to the exception handler. Exception 13 will though.
  882.  
  883.   Ofcourse these are all changes to the raw/XMS/VCPI side of the system. DPMI
  884. will still work exactly as it did in 1.29a. Under DPMI, there is no
  885. distinction between V86 mode and actual real mode. Thus, INTs 32h and 33h are
  886. handled in exactly the same way as INTs 34h and 35h. It comes out to be a more
  887. consistent int/IRQ system:
  888.  
  889. ) Under raw/XMS/VCPI:
  890.   ) IRQs that occur in protected mode will go directly to their protected mode
  891.     handlers, where they can be sent on to their real mode handlers or not.
  892.   ) IRQs that occur in a real mode safe call (INT32/33) will not make it to
  893.     the pmode handlers, but will go directly to their real mode handlers.
  894.   ) IRQs that occur in a real mode V86 call (INT34/35) will go on to the
  895.     pmode handlers, where they can be sent on to their real mode handlers or
  896.     not.
  897. ) Under DPMI:
  898.   ) IRQs should ALWAYS go to the protected mode handler first, then may be
  899.     sent on to their real mode handlers or not. However, DPMI implementations
  900.     out there now are far from perfect, and may not always do that. They might
  901.     separate the two IRQ systems (IRQs in real mode go ONLY to their real mode
  902.     handlers. IRQs in pmode go ONLY to their pmode handlers, and cannot be
  903.     sent on to real mode).
  904.  
  905.  
  906. ------------------------------------------------------------------------------
  907. 2.2x333 over 2.1232.
  908.  
  909.   I've added two things to PMODE since 2.1232 that might be useful in many
  910. situations. The ability to allocate and set the base addx of your own
  911. selectors. And a built in function to set up a REAL real mode IRQ callback
  912. to protected mode IRQs.
  913.  
  914.   You can only allocate new data selectors, not code. This is provided for
  915. those instances where you may want to use 16bit addresses with your data. This
  916. comes in useful in very tight routines where you need to use the low 16bits of
  917. a register in addressing and you need the upper 16 bits for something else.
  918. You may change any of the segregs during execution to any other valid data
  919. selector except SS. Don't mess with SS or you'll probably regret it. Just be
  920. sure you set them back to normal before calling any PMODE functions or INTs.
  921. Except for FS, which is now a totally free selector, except for returns from
  922. INTs where it will be passed back as '_seldata'.
  923.  
  924.   The IRQ thing is there just to make life a little easier. Since you will
  925. probably need to use INT33 not 35 for stuff like file calls (critical under
  926. VCPI), your protected mode IRQ handlers will no longer respond to IRQs that
  927. occur during that call. The function '_rmpmirqset' solves this problem by
  928. setting up a real mode IRQ handler that calls the protected mode IRQ handler
  929. for that IRQ. This means that no matter if in real mode, V86 executing a real
  930. mode call, or protected mode, your pmode IRQ handlers will get all their IRQs.
  931.  
  932.   '_rmpmirqset' sets up a little stub of code in a 21 byte buffer you provide
  933. for it. This code is both the real mode call to a protected mode routine, and
  934. that protected mode routine which calls the interrupt routine correctly. The
  935. address of the protected mode IRQ handler is obtained using '_getirqvect' only
  936. in '_rmpmirqset', and is put into the code stub. This means that if you change
  937. your protected mode handler, the real mode callback will still call the old
  938. handler. To remedy this, you need merely call '_rmpmirqset' again with the
  939. same buffer. But ignore the addx of the old real mode handler it passes back,
  940. because it will be the addx of the old callback. Also make sure that the 21
  941. byte buffer you provide is in low memory, because it must be callable from
  942. real mode. The HMA is not considered low memory for this purpose because the
  943. real mode seg:off of the buffer is normalized.
  944.  
  945. ------------------------------------------------------------------------------
  946. New functions to 2.2x333.
  947.  
  948. _getselector:near
  949.   Allocate a selector
  950.   Out:
  951.     CF=1 - selector not allocated
  952.     CF=0 - selector allocated
  953.       AX - 4G data selector or ?
  954.   Notes:
  955.     The selector returned is for a 4G r/w data segment with an undefined base
  956.      address.
  957.  
  958. _setselector:dword
  959.   Set the base addx for a selector
  960.   In:
  961.     AX - selector
  962.     EDX - linear base addx for selector
  963.  
  964. _freeselector:near
  965.   Free an allocated selector
  966.   In:
  967.     AX - selector
  968.  
  969. _rmpmirqset:near
  970.   Set a real mode IRQ vect to redirect to pmode
  971.   In:
  972.     BL - IRQ number
  973.     EDI -> 21 byte buffer for code stub created
  974.   Out:
  975.     EDX - old seg:off of real mode IRQ handler
  976.  
  977. _rmpmirqfree:near
  978.   Reset a real more IRQ vect back to normal
  979.   In:
  980.     BL - IRQ number
  981.     EDX - seg:off of real mode IRQ handler
  982.  
  983. ------------------------------------------------------------------------------
  984. Notes for v2.2x333:
  985.  
  986. ) SELECTORS is the maximum number of selectors you will need to allocate at
  987. any one time in your code.
  988.  
  989. ) You do not have to free any selectors you allocated before exiting.
  990.  
  991. ) FS does not need to be _seldata in calls to PMODE functions or INTs,
  992. although INTs will return with FS=_seldata.
  993.  
  994. ) Remember, under a non-buggy DPMI INT33 will not give up PMODE control. So
  995. '_rmpmirqset' will have no effect, but it will not hurt.
  996.  
  997. ) Also remember that '_rmpmirqset' sets the real mode IRQ callback to call the
  998. protected mode IRQ handler which was set when you made the call to
  999. '_rmpmirqset'.
  1000.  
  1001. ) You must NOT chain back to any real mode IRQs from any protected mode IRQs
  1002. that you have used '_rmpmirqset' on. Because the vector in the real mode
  1003. interrupt vector table for that real mode IRQ now points to the callback to
  1004. protected mode (Infinite loop thingy, get it?). If you must, do yourself a
  1005. short little real mode routine that calls the old real mode interrupt handler,
  1006. and call that with INT32/34 from within you protected mode IRQ handler.
  1007.  
  1008. ) Before jumping to _exit, you must free all real mode IRQ vects that you set
  1009. with '_rmpmirqset' using '_rmpmirqfree' or just by putting its old address
  1010. back into the real mode int vect table.
  1011.  
  1012. ------------------------------------------------------------------------------
  1013. Oh well...:
  1014.  
  1015. It still seems to function... And its total garbage by now... L8r...
  1016.  
  1017.   Tran ... (Thomas Pytel).
  1018.  
  1019. Greets to all my friends, and all the coders of the world...
  1020. Also, to all demo people (artists, muzicians, other coders, etc...)...
  1021.  
  1022. You can't reach me anywhere, so don't try...
  1023.  
  1024.